package com.hezhiqin.formework.context;

import com.hezhiqin.formework.annotation.HZQAutowired;
import com.hezhiqin.formework.annotation.HZQController;
import com.hezhiqin.formework.annotation.HZQService;
import com.hezhiqin.formework.aop.HZQAopProxy;
import com.hezhiqin.formework.aop.HZQCglibAopProxy;
import com.hezhiqin.formework.aop.HZQJdkDynamicAopProxy;
import com.hezhiqin.formework.aop.config.HZQAopConfig;
import com.hezhiqin.formework.aop.support.HZQAdvisedSupport;
import com.hezhiqin.formework.beans.HZQBeanFactory;
import com.hezhiqin.formework.beans.HZQBeanWrapper;
import com.hezhiqin.formework.beans.config.HZQBeanDefinition;
import com.hezhiqin.formework.beans.config.HZQBeanPostProcessor;
import com.hezhiqin.formework.beans.support.HZQBeanDefinitionReader;
import com.hezhiqin.formework.beans.support.HZQDefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: homeWork
 * @description:
 * @author: hezhiqin
 * @create: 2019-10-27 13:07
 */
public class HZQApplicationcontext extends HZQDefaultListableBeanFactory implements HZQBeanFactory {

    private String[] configLocations;

    //单例的的IOC容器
    private  Map<String,Object> singletonObject= new ConcurrentHashMap<String,Object>();

    //通用的IOC容器
    private  Map<String,HZQBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String,HZQBeanWrapper>();

    HZQBeanDefinitionReader hzqBeanDefinitionReader;


    public HZQApplicationcontext(String... configLocations) {

        this.configLocations =configLocations;

        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


     public  String[] getBeanDefinitionNames(){
         Set<String> set = beanDefinitionMap.keySet();
         return  set.toArray(new String[this.beanDefinitionMap.size()]);
     }

    int getBeanDefinitionCount(){
        return  beanDefinitionMap.size();
    }

    //初始化
    @Override
    protected void refresh() throws ClassNotFoundException {
        //1.ioc第一步，定位配置文件
        hzqBeanDefinitionReader = new HZQBeanDefinitionReader(configLocations);


        //2.加载配置文件，扫描相关的类，封装成BeanDefinition
        List<HZQBeanDefinition> hzqBeanDefinitions =  hzqBeanDefinitionReader.loadBeanDefinitions();

        //3.注册，配置信息放入容器中（伪IOC容器，只保存了配置信息，真正的IOC在beanWapper）
        doRegisterBeanDefinition(hzqBeanDefinitions);

        //4.把不是延时加载的类，提前的初始化
        doAutoWrited();
    }

    private void doRegisterBeanDefinition(List<HZQBeanDefinition> hzqBeanDefinitions) {

        for (HZQBeanDefinition  hzqBeanDefinition: hzqBeanDefinitions) {
            super.beanDefinitionMap.put(hzqBeanDefinition.getFactoryBeanName(),hzqBeanDefinition);
        }


    }

    private void doAutoWrited() {
        for (Map.Entry<String,HZQBeanDefinition>  beanDefinitionEntry: super.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();

            if (!beanDefinitionEntry.getValue().isLazyInit()){
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }

    }



    @Override
    public Object getBean(String beanName) throws Exception {


        //AOP中的前置处理 和 后置处理
        HZQBeanPostProcessor hzqBeanPostProcessor = new HZQBeanPostProcessor();


        //1.初始化
        Object instantiateBean = instantiateBean(beanName,this.beanDefinitionMap.get(beanName));


        //对象封装到BeanWrapper中
        HZQBeanWrapper hzqBeanWrapper = new HZQBeanWrapper(instantiateBean);




        hzqBeanPostProcessor.postProcessBeforeInitialization(instantiateBean,beanName);




        //2.将BeanWrapper 保存的IOC 容器中
        if (!factoryBeanInstanceCache.containsKey(beanName)){
            factoryBeanInstanceCache.put(beanName,hzqBeanWrapper);
        }


        hzqBeanPostProcessor.postProcessAfterInitialization(instantiateBean,beanName);

        //3.注入
        populateBean(beanName,this.beanDefinitionMap.get(beanName),hzqBeanWrapper);


        return factoryBeanInstanceCache.get(beanName).getWrappedInstance();

    }


    //注入
    private void populateBean(String beanName, HZQBeanDefinition hzqBeanDefinition, HZQBeanWrapper hzqBeanWrapper) {
        //1、拿到实例
        Object object = hzqBeanWrapper.getWrappedInstance();

        //2.判断只有加了注解的类，才执行注入
        Class aClass = hzqBeanWrapper.getWrappedClass();

        //如果不是
        if (!(aClass.isAnnotationPresent(HZQController.class)||aClass.isAnnotationPresent(HZQService.class))){
            return;
        }

        Field[] fields = aClass.getDeclaredFields();

        for (Field field: fields) {
            if (!field.isAnnotationPresent(HZQAutowired.class)){continue;}
            //判断这个字段有没有Auto
            HZQAutowired hzqAutowired =  field.getAnnotation(HZQAutowired.class);

            String autowiredBeanName =  hzqAutowired.value().trim();

            if ("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getSimpleName();
            }

            field.setAccessible(true);
            try {
                //为什么会为null?因为循环顺序的问题
                if (this.factoryBeanInstanceCache.get(autowiredBeanName)==null){
                    try {
                        field.set(object,Class.forName(this.beanDefinitionMap.get(autowiredBeanName).getBeanClassName()).newInstance());
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }else {
                    field.set(object,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
                }


            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private Object instantiateBean(String beanName, HZQBeanDefinition hzqBeanDefinition) {

        //1.拿到要实例化的对象的类名
        String className =  hzqBeanDefinition.getBeanClassName();



        //2.反射实例化，得到对象
        Object instance = null;

        try {
            //假设默认单例
            if (singletonObject.containsKey(className)){
                instance = this.singletonObject.get(className);
            }else {
                Class<?>  aClass = Class.forName(className);
                instance = aClass.newInstance();
                //初始化AOP配置信息
                HZQAdvisedSupport config = instantionAopConfig(hzqBeanDefinition);
                config.setTargetClass(aClass);
                config.setTarget(instance);

                //符合PointCut的规则的话，创建代理对象
                if(config.pointCutMatch()) {
                    instance = createProxy(config).getProxy();
                }

                this.singletonObject.put(className,instance);
                this.singletonObject.put(hzqBeanDefinition.getFactoryBeanName(),instance);
            }

        }catch (Exception e){
            e.printStackTrace();
        }

        return  instance;
    }

    private HZQAopProxy createProxy(HZQAdvisedSupport config) {
        Class targetClass  = config.getTargetClass();
        if (targetClass.getInterfaces().length>0){
            return new HZQJdkDynamicAopProxy(config);
        }
        return new HZQCglibAopProxy(config);
    }

    private HZQAdvisedSupport instantionAopConfig(HZQBeanDefinition hzqBeanDefinition) {

        HZQAopConfig hzqAopConfig = new HZQAopConfig();
        hzqAopConfig.setPointCut(this.hzqBeanDefinitionReader.getConfigContext().getProperty("pointCut"));
        hzqAopConfig.setAspectClass(this.hzqBeanDefinitionReader.getConfigContext().getProperty("aspectClass"));
        hzqAopConfig.setAspectBefore(this.hzqBeanDefinitionReader.getConfigContext().getProperty("aspectBefore"));
        hzqAopConfig.setAspectAfter(this.hzqBeanDefinitionReader.getConfigContext().getProperty("aspectAfter"));
        hzqAopConfig.setAspectAfterThrow(this.hzqBeanDefinitionReader.getConfigContext().getProperty("aspectAfterThrow"));
        hzqAopConfig.setAspectAfterThrowingName(this.hzqBeanDefinitionReader.getConfigContext().getProperty("aspectAfterThrowingName"));
        return new HZQAdvisedSupport(hzqAopConfig);

    }

    public Properties getCongif(){
        return this.hzqBeanDefinitionReader.getConfigContext();
    }
}
